#include "ObjLoader.h"

#include <iostream>
#include <fstream>

using namespace kp;
using namespace std;

ObjLoader::ObjLoader(const std::string& fileName) {
	// Get mtllib
    std::ifstream in(fileName);

    if (!in.good())
    {
        cout << "ERROR: loading obj:(" << fileName << ") file is not good" << "\n";
        exit(0);
    }

    char buffer[256], str[255];
    char mtllib[256];
    Point3f ver;
    Normal nor;
    Point2f uv;
    Face f1, f2, f3;

    while (!in.getline(buffer, 255).eof())
    {
        buffer[255] = '\0';

        sscanf_s(buffer, "%s", str, 255);

        if (buffer[0] == 'm' && buffer[1] == 't') {
            if (sscanf(buffer, "mtllib %s", mtllib) == 1) {
                // TODO: load .mtl file
                cout << "mtllib path: " << mtllib << endl;
            }
            else {
                cerr << "ERROR: mtllib path not in wanted format" << endl;
            }
        }
        else if (buffer[0] == 'v' && (buffer[1] == ' ' || buffer[1] == 32)) {
            if (sscanf(buffer, "v %f %f %f", &ver.x, &ver.y, &ver.z) == 3) {
                mesh.vertices.push_back(ver);
            }
            else {
                cerr << "ERROR: vertex not in wanted format in OBJLoader" << endl;
            }
        }
        else if (buffer[0] == 'v' && buffer[1] == 't') {
            if (sscanf(buffer, "vt %f %f", &uv.x, &uv.y) == 2) {
                mesh.uvs.push_back(uv);
            }
            else {
                cerr << "ERROR: texCoord not in wanted format in OBJLoader" << endl;
            }
        }
        else if (buffer[0] == 'v' && buffer[1] == 'n') {
            if (sscanf(buffer, "vn %f %f %f", &nor.x, &nor.y, &nor.z) == 3) {
                mesh.normals.push_back(nor);
            }
            else {
                cerr << "ERROR: normal not in wanted format in OBJLoader" << endl;
            }
        }
        // reading FaceMtls   
        else if (buffer[0] == 'f' && (buffer[1] == ' ' || buffer[1] == 32))
        {
            int nt = sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d",
                &f1.v, &f1.t, &f1.n,
                &f2.v, &f2.t, &f2.n,
                &f3.v, &f3.t, &f3.n
            );
            mesh.faceindices.push_back(f1 - Point3i(1, 1, 1));
            mesh.faceindices.push_back(f2 - Point3i(1, 1, 1));
            mesh.faceindices.push_back(f3 - Point3i(1, 1, 1));
            if (nt != 9)
            {
                cerr << "ERROR: I don't know the format of that FaceMtl" << endl;
            }
        }
    }
}

std::vector<Ref<Primitive>> ObjLoader::get(Ref<Transform> o2w) {
    printf("model info>> v: %lld, t: %lld, n: %lld, f: %lld\n",
        mesh.vertices.size(),
        mesh.uvs.size(),
        mesh.normals.size(),
        mesh.faceindices.size() / 3);

    int nTriangles = (int)mesh.faceindices.size() / 3;
    int nVertices = (int)mesh.vertices.size();
    auto tris = Triangle::CreateTriangleMesh(o2w, MakeRef<Transform>(o2w->inverse()), false,
        nTriangles, nVertices,
        mesh.faceindices.data(),
        mesh.vertices.data(),
        mesh.normals.data(),
        nullptr,
        mesh.uvs.data());
    std::vector<Ref<Primitive>> ret;
    for (auto& tri : tris) {
        ret.push_back(kp::MakeRef<kp::GeometryPrimitive>(kp::MakeRef<kp::MatteMaterial>(kp::MakeRef<kp::ConstantTexture>(kp::Spectrum(1, 1, 1))), tri));
    }
    return ret;
}
